﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using GrapeCity.PAutoTester.Core;
using System.Collections;

namespace GrapeCity.PAutoTester.GUI
{
    internal class TestAssemblyTreeView : TreeView
    {
        #region Properties
        private ImageList treeViewImages;
        private ContextMenuStrip contextMenuStrip;
        private ToolStripMenuItem collapseMenuItem;
        private ToolStripMenuItem expandMenuItem;
        private System.ComponentModel.IContainer components;
        private Hashtable _treeMap = new Hashtable();
        #endregion

        #region Constructor
        public TestAssemblyTreeView()
        {
            InitializeComponent();

            this.CheckBoxes = true;
            this.ImageList = this.treeViewImages;
            this.ImageIndex = this.SelectedImageIndex = 0;
            this.ContextMenuStrip = this.contextMenuStrip;
        }

        #endregion

        #region Methods
        private void InitializeComponent()
        {
            this.components = new System.ComponentModel.Container();
            System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(TestAssemblyTreeView));
            this.treeViewImages = new System.Windows.Forms.ImageList(this.components);
            this.contextMenuStrip = new System.Windows.Forms.ContextMenuStrip(this.components);
            this.collapseMenuItem = new System.Windows.Forms.ToolStripMenuItem();
            this.expandMenuItem = new System.Windows.Forms.ToolStripMenuItem();
            this.contextMenuStrip.SuspendLayout();
            this.SuspendLayout();
            // 
            // treeViewImages
            // 
            this.treeViewImages.ImageStream = ((System.Windows.Forms.ImageListStreamer)(resources.GetObject("treeViewImages.ImageStream")));
            this.treeViewImages.TransparentColor = System.Drawing.Color.Transparent;
            this.treeViewImages.Images.SetKeyName((int)ImageIndexs.Default, "default.png");
            this.treeViewImages.Images.SetKeyName((int)ImageIndexs.Success, "success.png");
            this.treeViewImages.Images.SetKeyName((int)ImageIndexs.Failure, "fail.png");
            this.treeViewImages.Images.SetKeyName((int)ImageIndexs.Ignore, "ignore.png");
            // 
            // contextMenuStrip
            // 
            this.contextMenuStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
            this.collapseMenuItem,
            this.expandMenuItem});
            this.contextMenuStrip.Name = "contextMenuStrip";
            this.contextMenuStrip.Size = new System.Drawing.Size(137, 48);
            // 
            // collapseMenuItem
            // 
            this.collapseMenuItem.Name = "collapseMenuItem";
            this.collapseMenuItem.Size = new System.Drawing.Size(136, 22);
            this.collapseMenuItem.Text = "Collapse All";
            this.collapseMenuItem.Click += new System.EventHandler(this.collapseMenuItem_Click);
            // 
            // expandMenuItem
            // 
            this.expandMenuItem.Name = "expandMenuItem";
            this.expandMenuItem.Size = new System.Drawing.Size(136, 22);
            this.expandMenuItem.Text = "Expand All";
            this.expandMenuItem.Click += new System.EventHandler(this.expandMenuItem_Click);
            this.contextMenuStrip.ResumeLayout(false);
            this.ResumeLayout(false);
        }

        public void Load(ITestAssembly testAssembly, UIListener listener)
        {
            if (testAssembly == null)
                throw new ArgumentNullException("test assembly");
            if (testAssembly.TestClasses.Count == 0)
                throw new InvalidOperationException("The assembly has no test classes");
            if (listener == null)
                throw new ArgumentNullException("listener");

            this.Clear();

            TestAssemblyNode assemblyNode = new TestAssemblyNode(testAssembly, listener);
            foreach (var testClass in testAssembly.TestClasses)
            {
                TestClassNode classNode = new TestClassNode(testClass, listener);
                assemblyNode.Nodes.Add(classNode);
                this._treeMap.Add(classNode.FullName, classNode);

                foreach (var method in testClass.TestMethods)
                {
                    TestMethodNode methodNode = new TestMethodNode(method, listener);
                    classNode.Nodes.Add(methodNode);
                    this._treeMap.Add(methodNode.FullName, methodNode);
                }
            }
            this.Nodes.Add(assemblyNode);
            this._treeMap.Add(assemblyNode.FullName, assemblyNode);
        }

        public void Clear()
        {
            this._treeMap.Clear();
            this.Nodes.Clear();
        }

        public void Reset()
        {
            if (this.Nodes != null && this.Nodes.Count > 0)
            {
                foreach (TreeNode node in this.Nodes)
                {
                    ResetInternal(node);
                }
            }
        }

        private void ResetInternal(TreeNode node)
        {
            node.ImageIndex = node.SelectedImageIndex = 0;
            if (node is ITestNode)
                ((ITestNode)node).TestResult = null;

            if (node.Nodes != null && node.Nodes.Count > 0)
            {
                foreach (TreeNode nd in node.Nodes)
                {
                    ResetInternal(nd);
                }
            }
        }

        public void CheckAll()
        {
            if (this.Nodes != null && this.Nodes.Count > 0)
            {
                foreach (TreeNode node in this.Nodes)
                {
                    CheckAllInternal(node);
                }
            }
        }

        private void CheckAllInternal(TreeNode node)
        {
            node.Checked = true;
            if (node.Nodes != null && node.Nodes.Count > 0)
            {
                foreach (TreeNode nd in node.Nodes)
                {
                    CheckAllInternal(nd);
                }
            }
        }

        /// <summary>
        ///  Raises the <see cref="E:System.Windows.Forms.TreeView.AfterCheck"/> event.
        ///  UpDate test assemby, test class or test method's ShouldRun property
        /// </summary>
        /// <param name="e">A <see cref="T:System.Windows.Forms.TreeViewEventArgs"/> that contains the event data.</param>
        protected override void OnAfterCheck(TreeViewEventArgs e)
        {
            base.OnAfterCheck(e);

            TreeNode checkedNode = e.Node;
            bool isChecked = checkedNode.Checked;
            if (checkedNode is TestAssemblyNode && e.Action != TreeViewAction.Unknown)
            {
                //Update all its child nodes
                foreach (var node in ((TestAssemblyNode)checkedNode).Nodes)
                {
                    TestClassNode classNode = node as TestClassNode;
                    if (classNode != null)
                    {
                        classNode.Checked = isChecked;
                        foreach (var n in classNode.Nodes)
                        {
                            TestMethodNode methodNode = n as TestMethodNode;
                            if (methodNode != null)
                                methodNode.Checked = isChecked;
                        }
                    }
                }
            }

            if (checkedNode is TestClassNode && e.Action != TreeViewAction.Unknown)
            {
                //Update all is child nodes
                foreach (var node in ((TestClassNode)checkedNode).Nodes)
                {
                    TestMethodNode methodNode = node as TestMethodNode;
                    if (methodNode != null)
                        methodNode.Checked = isChecked;
                }

                //Update its parent nodes
                UpdateParentNode((TestClassNode)checkedNode, isChecked);
            }

            if (checkedNode is TestMethodNode && e.Action != TreeViewAction.Unknown)
            {
                TestClassNode parent = checkedNode.Parent as TestClassNode;
                if (parent != null)
                {
                    UpdateParentNode((TestMethodNode)checkedNode, isChecked);
                    if (parent.Parent != null && parent.Parent is TestAssemblyNode)
                        UpdateParentNode(parent, parent.Checked);
                }
            }
        }

        private void UpdateParentNode(TestClassNode checkedNode, bool isChecked)
        {
            if (checkedNode.Parent is TestAssemblyNode)
            {
                if (isChecked)
                    (checkedNode.Parent).Checked = true;
                else //UnChecked
                {
                    bool flag = true;
                    foreach (var item in checkedNode.Parent.Nodes)
                    {
                        if (item is TestClassNode)
                        {
                            if (((TestClassNode)item).Checked)
                            {
                                flag = false;
                                break;
                            }
                        }
                    }
                    if (flag)
                        (checkedNode.Parent).Checked = false;
                }
            }
        }

        private void UpdateParentNode(TestMethodNode checkedNode, bool isChecked)
        {
            if (checkedNode.Parent is TestClassNode)
            {
                if (isChecked)
                    (checkedNode.Parent).Checked = true;
                else //UnChecked
                {
                    bool flag = true;
                    foreach (var item in checkedNode.Parent.Nodes)
                    {
                        if (item is TestMethodNode)
                        {
                            if (((TestMethodNode)item).Checked)
                            {
                                flag = false;
                                break;
                            }
                        }
                    }
                    if (flag)
                        (checkedNode.Parent).Checked = false;
                }
            }
        }

        public ImageIndexs GetImageListIndex(TestResult result)
        {
            if (this.ImageList != null)
            {
                if (result == null)
                    return ImageIndexs.Default;

                if (!result.Executed)
                    return ImageIndexs.Ignore;

                if (result.IsSuccess)
                    return ImageIndexs.Success;

                if (result.IsFailure)
                    return ImageIndexs.Failure;
            }
            return ImageIndexs.None;
        }

        public void ImportTestResult(TestAssemblyResult assemblyResult)
        {
            if (assemblyResult == null)
                throw new ArgumentNullException("assembly result");

            if (this._treeMap.ContainsKey(assemblyResult.FullName))
            {
                ITestNode assemblyNode = this._treeMap[assemblyResult.FullName] as ITestNode;
                assemblyNode.Checked = true;
                assemblyNode.TestResult = assemblyResult;

                assemblyNode.ImageIndex = assemblyNode.SelectedImageIndex = (int)this.GetImageListIndex(assemblyResult);
            }

            if (assemblyResult.Results != null && assemblyResult.Results.Count > 0)
            {
                foreach (TestClassResult classResult in assemblyResult.Results)
                {
                    if (this._treeMap.ContainsKey(classResult.FullName))
                    {
                        ITestNode classNode = this._treeMap[classResult.FullName] as ITestNode;
                        classNode.TestResult = classResult;
                        classNode.Checked = true;
                        classNode.ImageIndex = classNode.SelectedImageIndex = (int)this.GetImageListIndex(classResult);

                        if (classResult.Results != null && classResult.Results.Count > 0)
                        {
                            foreach (TestMethodResult methodResult in classResult.Results)
                            {
                                if (this._treeMap.ContainsKey(methodResult.FullName))
                                {
                                    ITestNode methodNode = this._treeMap[methodResult.FullName] as ITestNode;
                                    methodNode.TestResult = methodResult;
                                    methodNode.Checked = true;
                                    methodNode.ImageIndex = methodNode.SelectedImageIndex = (int)this.GetImageListIndex(methodResult);
                                }
                            }
                        }
                    }
                }
            }
        }

        #region Context Menu
        /// <summary>
        ///   When Expand context menu item is clicked, expand the node
        /// </summary>
        private void expandMenuItem_Click(object sender, System.EventArgs e)
        {
            this.BeginUpdate();
            this.ExpandAll();
            this.EndUpdate();
        }

        /// <summary>
        ///   When Collapse context menu item is clicked, collapse the node
        /// </summary>
        private void collapseMenuItem_Click(object sender, System.EventArgs e)
        {
            this.BeginUpdate();
            this.CollapseAll();
            this.EndUpdate();
        }

        private void contextMenuStrip_Opening(object sender, System.ComponentModel.CancelEventArgs e)
        {
            if (this.Nodes.Count == 0)
                e.Cancel = true;
        }
        #endregion

        #endregion
    }

    internal enum ImageIndexs
    {
        None = -1,
        Default = 0,
        Success = 1,
        Failure = 2,
        Ignore = 3,
    }
}
